﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SharpShare.Collections;

namespace SharpShare.Storage {
    public static class StorageExtensions {
        public static bool IsContainedWithin(this IStorageItem item, IStorageContainer container) {
            if (item == null) throw new ArgumentNullException("item");
            if (container == null) throw new ArgumentNullException("container");

            IStorageItem current = item;
            while (current != null) {
                if (current.Equals(container))
                    return true;

                current = current.Parent;
            }

            return false;
        }

        public static IEnumerable<IStorageItem> ListAllContents(this IStorageContainer container) {
            if (container == null) throw new ArgumentNullException("container");

            List<IStorageItem> allChildren = null;

            try {
                allChildren = container.Contents.ToList();
            } catch {

            }

            if (allChildren == null) {
                yield break;
            }

            foreach (IStorageItem child in allChildren) {
                yield return child;

                IStorageContainer childContainer = child.AsContainer();

                if (childContainer != null) {
                    foreach (IStorageItem childItem in childContainer.ListAllContents()) {
                        yield return childItem;
                    }
                }
            }
        }
        public static Stream CreateStream(this IStorageDataProvider dataProvider, bool ownsStream = true) {
            if (dataProvider == null) throw new ArgumentNullException("dataProvider");
            return new StorageDataStream(dataProvider, ownsStream);
        }

        public static IStorageItem ChangeParent(this IStorageItem item, IStorageContainer newParent) {
            if (item == null) throw new ArgumentNullException("item");

            if (item.Parent != null && newParent != null) {
                if (item.Parent.Equals(newParent)) {
                    return item;
                }
            } else if (item.Parent == null && newParent == null) {
                return item;
            }

            return Filter.FilteredStorageItem.Create(item, Filter.StorageFilter.Empty, newParent);
        }

        public static IStorageProvider GetStorageProvider(this IStorageItem item) {
            IStorageProvider provider = item
                .WalkHierarchy()
                .OfContainers()
                .Select(c => c.As<IStorageProvider>())
                .Where(c => c != null)
                .FirstOrDefault();
            return provider;
        }

        private static IEnumerable<IStorageItem> WalkHierarchy(this IStorageItem item) {
            IStorageItem current = item;

            while (current != null) {
                yield return current;
                current = current.Parent;
            }
        }

        public static void Rename(this IStorageItem item, string name) {
            item.Move(item.Parent, name);
        }

        public static Path<IStorageItem> CreatePath(this IStorageItem node) {
            if (node == null) throw new ArgumentNullException("node");

            Path<IStorageItem> path = Path<IStorageItem>.Empty;

            IStorageItem current = node;

            while (current != null) {
                path = (path + current);
                current = current.Parent;
            }

            return path;
        }

        public static Path<string> CreateNamePath(this IStorageItem node) {
            Path<IStorageItem> path = node.CreatePath();
            Path<string> namePath = Path<string>.Empty;

            foreach (IStorageItem comp in path.Components) {
                namePath = (comp.Name + namePath);
            }

            return namePath;
        }
        public static IStorageItem Locate(this IStorageContainer container, Path<string> path) {
            if (container == null) throw new ArgumentNullException("container");
            if (path == null) throw new ArgumentNullException("path");

            IStorageItem current = container;

            foreach (string name in path.Components) {
                current = current.DemandContainer().Contents[name];
                if (current == null || !current.Exists) {
                    return null;
                }
            }

            return current;
        }

        public static IStorageContainer AsContainer(this IStorageItem item) {
            if (item == null) {
                return null;
            }

            return item.As<IStorageContainer>();
        }
        public static IStorageFile AsFile(this IStorageItem item) {
            if (item == null) {
                return null;
            }

            return item.As<IStorageFile>();
        }

        public static T DemandExists<T>(this T item) where T : IStorageItem {
            if (item == null || !item.Exists) {
                throw new StorageItemNotFoundException();
            }

            return item;
        }
        public static IStorageContainer DemandContainer(this IStorageItem item) {
            if (item == null || !item.Exists) {
                throw new StorageItemNotFoundException();
            }

            IStorageContainer container = item.AsContainer();
            if (container == null) {
                throw new StorageItemInvalidTypeException();
            }

            return container;
        }
        public static IStorageFile DemandFile(this IStorageItem item) {
            if (item == null || !item.Exists) {
                throw new StorageItemNotFoundException();
            }

            IStorageFile file = item.AsFile();
            if (file == null) {
                throw new StorageItemInvalidTypeException();
            }

            return file;
        }

        public static IEnumerable<IStorageContainer> OfContainers(this IEnumerable<IStorageItem> items) {
            return items.Select(i => i.AsContainer()).Where(i => i != null);
        }
        public static IEnumerable<IStorageFile> OfFiles(this IEnumerable<IStorageItem> items) {
            return items.Select(i => i.AsFile()).Where(i => i != null);
        }

        public static void CopyAttributes(this IStorageItem item, IStorageItem destinationItem) {
            foreach (AttributeCopyAction copyAction in ListAttributeCopyActions()) {
                try {
                    copyAction(item, destinationItem);
                } catch { }
            }
        }

        public static IStorageFile Copy(this IStorageFile file, IStorageContainer destinationContainer, string newNameOrNull = null) {
            if (file == null) {
                throw new ArgumentNullException("file");
            }
            if (destinationContainer == null) {
                throw new ArgumentNullException("destinationContainer");
            }

            using (StorageDataStream sourceStream = new StorageDataStream(file.Open(StorageFileAccess.Read, StorageFileAccess.Read))) {
                string name;

                if (string.IsNullOrEmpty(newNameOrNull)) {
                    name = file.Name;
                } else {
                    name = newNameOrNull;
                }

                IStorageFile newFile = destinationContainer.CreateFile(name);

                if (newFile == null) {
                    throw new StorageItemNotFoundException();
                }

                using (StorageDataStream destinationStream = new StorageDataStream(newFile.Open(StorageFileAccess.Write, StorageFileAccess.None))) {
                    foreach (AttributeCopyAction copyAction in ListAttributeCopyActions()) {
                        try {
                            copyAction(file, newFile);
                        } catch { }
                    }

                    sourceStream.CopyTo(destinationStream, 102400);
                }

                return newFile;
            }
        }

        private delegate void AttributeCopyAction(IStorageItem source, IStorageItem destination);

        private static IEnumerable<AttributeCopyAction> ListAttributeCopyActions() {
            yield return new AttributeCopyAction((source, destination) => {
                destination.DateCreated = source.DateCreated;
            });

            yield return new AttributeCopyAction((source, destination) => {
                destination.DateModified = source.DateModified;
            });

            yield return new AttributeCopyAction((source, destination) => {
                destination.Attributes = source.Attributes;
            });

            yield return new AttributeCopyAction((source, destination) => {
                var metadata = source.GetMetadata();
                destination.SetMetadata(metadata);
            });
        }

    }
}
